/*
 * Copyright (C) 2022 KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */

#include "desktopdataproviderprivate_p.h"
#include <KDesktopFile>
#include "tabletdesktopbackend.h"
#include "icontheme.h"
#include "iconprovider.h"
#include "appnamesort.h"
#include "desktopitemloader.h"
#include "desktopitemhelper.h"
#include "itempositioncalculator.h"
#include "database.h"
#include "utils.h"

namespace UKUI {

#define TABLET_DATABASE_DIR "/ukui/ukui-tablet-desktop/tablet"
#define EDUSCREEN_DATABSE_DIR "/ukui/ukui-tablet-desktop/eduscreen"
#define DATABASE_NAME "appList.db"

DesktopDataProviderPrivate::DesktopDataProviderPrivate(DesktopDataProvider *parent)
    : QObject{parent}
    , q_ptr(parent)
{
    registerMetaType();
    icon_theme = IconTheme::getInstance();
    std::tie(desktopRow, desktopColumn) = m_config.desktopLayout();


    if (m_config.desktopMode() == Config::Tablet) {
        m_appWidgetManager = new AppWidget::KAppWidgetManager(this);

        database = new DataBase(getDatabasePath(TABLET_DATABASE_DIR), this);
        m_desktopItemLoader = new TabletDesktopItemLoader(&m_desktopFileManager, &m_taskbarAppManager,
                                                          { desktopRow, desktopColumn }, m_appWidgetManager, this);
        initAppwidgetConnections();
    }
    if (m_config.desktopMode() == Config::EduScreen) {
        database = new DataBase(getDatabasePath(EDUSCREEN_DATABSE_DIR), this);
        m_desktopItemLoader = new EduScreenDesktopItemLoader(&m_desktopFileManager,
                                                             { desktopRow, desktopColumn }, this);
    }

    openDatabase();
    initConnections();

    if (database->firstInited()) {
        loadLocalItems();
        saveItemsToDatabase();
    } else {
        loadItemsFromDatabase();
        checkItems();
        placeItemsFromDatabase();
        loadNewInstalledApp();
        updateTaskbarItems();
    }
}

void DesktopDataProviderPrivate::initConnections()
{
    connect(icon_theme, &IconTheme::iconThemeChanged, this, &DesktopDataProviderPrivate::onIconThemeChanged);
    connect(&m_desktopFileManager, &DesktopFileManager::desktopFileAdded, this, &DesktopDataProviderPrivate::onDesktopFileAdded);
    connect(&m_desktopFileManager, &DesktopFileManager::desktopFileDeleted, this, &DesktopDataProviderPrivate::onDesktopFileDeteled);
    connect(&m_taskbarAppManager, &TaskbarAppManager::taskbarAppChanged, this, &DesktopDataProviderPrivate::onTaskbarAppChanged);
    connect(&m_appCategoryManager, &AppCategoryManager::softwareAppCategoryUpdated, this, &DesktopDataProviderPrivate::updateAllAppCategory);
    connect(&uninstaller, &Uninstaller::appUninstalled, this, &DesktopDataProviderPrivate::onAppUninstalled);
    connect(&uninstaller, &Uninstaller::appUninstallFailed, this, &DesktopDataProviderPrivate::onAppUninstallFailed);
}

void DesktopDataProviderPrivate::initAppwidgetConnections()
{
    connect(m_appWidgetManager, SIGNAL(appWidgetAdded(QString)),
            this, SLOT(onAppWidgetAdded(QString)));
    connect(m_appWidgetManager, SIGNAL(appWidgetDeleted(QString)),
            this, SLOT(onAppWidgetDeleted(QString)));
}

void DesktopDataProviderPrivate::onDesktopFileAdded(const QString &desktopFile)
{
    if (desktopFile.isEmpty()) {
        return;
    }
    addItemByDesktopFile(desktopFile);
}

void DesktopDataProviderPrivate::onDesktopFileDeteled(const QString &desktopFile)
{
    if (desktopFile.isEmpty()) {
        return;
    }
    removeItemByDesktopFile(desktopFile);
}

void DesktopDataProviderPrivate::onTaskbarAppChanged(const QStringList &taskbarDesktopName, TaskbarAppManager::TaskbarAction action)
{
    auto taskbarAppIdsAfterChanged = DesktopItemHelper::appIdsByDesktopFromAllItems(taskbarDesktopName, dataHolder.allItems);
    if (!dataHolder.itemIdsInSet.contains(kTaskbarGroupId) || dataHolder.itemIdsInSet.count() <= 0 ||
            dataHolder.itemIdsInSet.value(kTaskbarGroupId).at(0) == taskbarAppIdsAfterChanged) {
        return;
    }
    qDebug() << "原本任务栏列表为：" << dataHolder.itemIdsInSet.value(kTaskbarGroupId).at(0) <<"新的任务栏列表为:" << taskbarAppIdsAfterChanged;
    switch (action) {
    case TaskbarAppManager::Add:
        taskbarAppAdded(taskbarAppIdsAfterChanged);
        break;
    case TaskbarAppManager::Delete:
        taskbarAppDeleted(taskbarAppIdsAfterChanged);
        break;
    case TaskbarAppManager::Move:
        taskbarAppMoved(taskbarAppIdsAfterChanged);
        break;
    default: break;
    }
}

void DesktopDataProviderPrivate::onAppUninstalled(const QString &desktopFile)
{
    if (desktopFile.isEmpty()) {
        return;
    }
    removeItemByDesktopFile(desktopFile);
}

void DesktopDataProviderPrivate::onAppUninstallFailed(const QString &desktopFile)
{
    if (desktopFile.isEmpty()) {
        return;
    }
    addItemByDesktopFile(desktopFile);
}

void DesktopDataProviderPrivate::onAppWidgetAdded(const QString &appWidgetName)
{
    if (appWidgetName.isEmpty()) {
        return;
    }
    int itemId = DesktopItemHelper::appIdByNameFromAllItems(appWidgetName, dataHolder.allItems);
    if (itemId > 0) {
        return;
    }
    addWidgetItem(createWidgetItem(appWidgetName));
}

void DesktopDataProviderPrivate::onAppWidgetDeleted(const QString &appWidgetName)
{
    if (appWidgetName.isEmpty()) {
        return;
    }
    int itemId = DesktopItemHelper::appIdByNameFromAllItems(appWidgetName, dataHolder.allItems);
    if (itemId < 0 ||
            !dataHolder.appWidgetIds.contains(itemId)) {
        return;
    }
    removeWidgetByItemId(itemId);
}

void DesktopDataProviderPrivate::removeWidgetByItemId(const int itemId)
{
    Q_Q(DesktopDataProvider);
    int pageIndex = itemPageIndex(itemId);
    removeItemFromHomeScreen(itemId, pageIndex);
    Q_EMIT q->itemRemovedFromHomeScreen(itemId, pageIndex);
}

Widget* DesktopDataProviderPrivate::createWidgetItem(const QString &widgetName)
{
    QString qmlFilePath = m_appWidgetManager->getAppWidgetUiFile(widgetName);
    if (qmlFilePath.isEmpty()) {
        qWarning() << "Widget qml file path is empty.";
        return nullptr;
    }
    QString name = m_appWidgetManager->getAppWidgetConfig(widgetName, AppWidget::NAME);
    QString iconName = m_appWidgetManager->getAppWidgetConfig(widgetName, AppWidget::PREVIEW_PATH);
    int column = m_appWidgetManager->getAppWidgetConfig(widgetName, AppWidget::TARGET_CELL_WIDTH).toInt();
    int row = m_appWidgetManager->getAppWidgetConfig(widgetName, AppWidget::TARGET_CELL_HEIGHT).toInt();
    int id =  DesktopItemHelper::calculateIdForNewItem(dataHolder.allItems.keys());

    Widget *widget = new Widget(qmlFilePath, name, id, QList<int>()<<row<<column);
    return widget;
}

void DesktopDataProviderPrivate::addWidgetItem(Widget* appWidget)
{
    if (!appWidget) {
        return;
    }
    int widgetId = appWidget->getId();
    dataHolder.allItems[widgetId] = appWidget;
    dataHolder.appWidgetIds.append(widgetId);
    saveItemDetailsToDatabase(widgetId);
    addWidgetItemToHomePage(widgetId, appWidget->getPlaceHolder());
}

void DesktopDataProviderPrivate::addWidgetItemToHomePage(const int itemId,
                                                         const QList<int> &widgetPlaceHolder)
{
    Q_Q(DesktopDataProvider);
    if (widgetPlaceHolder.count() != 2) {
        return;
    }
    int widgetRow = widgetPlaceHolder.at(0);
    int widgetColumn = widgetPlaceHolder.at(1);
    AppWidgetPositionCalculator postionCalculator;
    int page, row, col;
    std::tie(page, row, col) =
            postionCalculator.calculationPosition(desktopRow, desktopColumn,
                                                  dataHolder.itemIdsInPageGrid,
                                                  widgetRow, widgetColumn);
    if (page < 0) {
        return;
    }
    dataHolder.allItemIdsInHomeScreen[page].append(itemId);
    updateItemIdsInPageGridFromHomeScreenIds(page);
    updateItemIdsInHomeScreenPageToDatabase(page);
    Q_EMIT q->itemAdded(itemId, page);
}

void DesktopDataProviderPrivate::registerMetaType()
{
    qRegisterMetaType<QMap<UKUI::BaseItem::Categories,QList<quint32> >>("QMap<UKUI::BaseItem::Categories,QList<quint32> >");
    qRegisterMetaType<QMap<int,QList<int> >>("QMap<int,QList<int> >");
    qRegisterMetaType<QMap<int,QList<QList<int> > >>("QMap<int,QList<QList<int> > >");
    qRegisterMetaType<QMap<UKUI::BaseItem::Categories,QList<UKUI::ItemId> >>("QMap<UKUI::BaseItem::Categories,QList<UKUI::ItemId> >");
}

void DesktopDataProviderPrivate::onIconThemeChanged()
{
    Q_Q(DesktopDataProvider);
    for (auto &item : dataHolder.allItems) {
        if (item->getType() == Type::Icon) {
            IconItem *appItem = static_cast<IconItem *>(item);
            QString absoluteDesktopName = appItem->getDesktopName();

            const KDesktopFile desktopFile(absoluteDesktopName);
            QString iconName = desktopFile.readIcon();
            QString iconId = IconProvider::iconIdWithExistedApp(iconName);
            appItem->setIconName(iconId);
        }
    }
    Q_EMIT q->iconThemeChanged();
}

QList<ItemId> DesktopDataProviderPrivate::sortAppIdAccordingLetter(QList<ItemId> &unorderedIdList)
{
    QMap<AppNameSort, UKUI::ItemId> orderedApp;
    foreach (auto &id, unorderedIdList) {
        if (dataHolder.allItems[id]->getType() == Type::Icon && !dataHolder.allItems[id]->getName().isEmpty()) {
            QString appName = dataHolder.allItems[id]->getName();
            orderedApp[AppNameSort(appName)] = id;
        }
    }
    return orderedApp.values();
}

void DesktopDataProviderPrivate::updateAllAppCategory()
{
    Q_Q(DesktopDataProvider);
    for (auto &item : qAsConst(dataHolder.allItems)) {
        IconItem *appItem = static_cast<IconItem *>(item);
        if (!appItem || appItem->getType() != Type::Icon) {
            continue;
        }
        QString desktopfpExecName = QSettings(appItem->getDesktopName(), QSettings::IniFormat).value("Desktop Entry/Exec").toString();
        desktopfpExecName = desktopfpExecName.mid(desktopfpExecName.lastIndexOf("/") + 1);
        desktopfpExecName = desktopfpExecName.left(desktopfpExecName.lastIndexOf(" "));

        if (!m_appCategoryManager.softwareAppCategory().keys().contains(desktopfpExecName)) {
            continue;
        }
        QList<int> appCategory = m_appCategoryManager.matchingAppCategories(m_appCategoryManager.softwareAppCategory().value(desktopfpExecName));
        if (appItem->getCategories() != appCategory) {
            appItem->setCategories(appCategory);
            database->updateAppItem(*appItem);
            qDebug() << "列表包含该应用，修改应用的分类" << appItem->getName() << appCategory;
                                                                appItem->setCategories(appCategory);
            emit q->appCategoryChanged(appItem->getId(), appCategory);
        }
    }
    //应用商店发出应用分类的信号比较晚
    //如果跟平板使用同一个信号,可能会看到大屏添加应用界面一直变化
    //所以没有跟平板使用同一个信号
    if (m_config.desktopMode() == Config::EduScreen) {
        emit q->eduCategoryChanged();
    }
}

void DesktopDataProviderPrivate::openDatabase()
{
    if (!database->openDatabase()) {
        qWarning() << "Open database failed.";
    }
}

void DesktopDataProviderPrivate::loadLocalItems()
{
    m_desktopItemLoader->load();
    dataHolder.allItems = m_desktopItemLoader->allItems();
    dataHolder.itemIdsInSet = m_desktopItemLoader->itemIdsInSet();
    dataHolder.allItemIdsInHomeScreen = m_desktopItemLoader->allItemIdsInHomeScreen();
    dataHolder.itemIdsInPageGrid = m_desktopItemLoader->itemIdsInPageGrid();
    dataHolder.appWidgetIds = m_desktopItemLoader->appWidgetIds();
}

int DesktopDataProviderPrivate::calculateIdForNewItem()
{
    auto ids = dataHolder.allItems.keys();
    for (int i=0; i<ids.count(); ++i) {
        if (i > 0 && ids.at(i) - ids.at(i-1) > 1) {
            return ids.at(i) - 1;
        }
    }
    return dataHolder.allItems.count() + 1;
}

void DesktopDataProviderPrivate::removeItem(int itemId)
{
    dataHolder.allItems.remove(itemId);
    database->deleteItemListData(itemId);
}

void DesktopDataProviderPrivate::removeItemByDesktopFile(const QString &desktopFile)
{
    Q_Q(DesktopDataProvider);
    int appId = DesktopItemHelper::appIdByDesktopFromAllItems(desktopFile, dataHolder.allItems);
    if (appId == -1) {
        return;
    }
    bool exists;
    int setId, pageIndex;
    std::tie(exists, setId, pageIndex) = DesktopItemHelper::itemPositionInSet(appId, dataHolder.itemIdsInSet);
    if (exists) {
        removeItemFromSet(appId, setId, pageIndex);
        Q_EMIT q->itemRemovedFromSet(appId, setId);
    } else {
        pageIndex = itemPageIndex(appId);
        removeItemFromHomeScreen(appId, pageIndex);
        Q_EMIT q->itemRemovedFromHomeScreen(appId, pageIndex);
    }
}

void DesktopDataProviderPrivate::addItemByDesktopFile(const QString &desktopFile)
{
    Q_Q(DesktopDataProvider);
    qDebug() << __func__ << desktopFile;
    auto *item = DesktopItemHelper::dissectInstallingDesktopFileToItem(desktopFile,
                                                                       m_appCategoryManager.softwareAppCategory(),
                                                                       dataHolder.allItems.keys());
    dataHolder.allItems[item->getId()] = item;
    AppItemPositionCalculator postionCalculator;

    int page, row, col;
    std::tie(page, row, col) =
            postionCalculator.calculationPosition(desktopRow, desktopColumn,
                                          dataHolder.itemIdsInPageGrid, 1, 1);
    qDebug() << __func__ << "position--------" << page << row <<col;

    if (m_config.desktopMode() == Config::Tablet) {
        placeItem(item->getId(), page, 1, 1);
        dataHolder.allItemIdsInHomeScreen[page].append(item->getId());
        updateItemIdsInPageGridFromHomeScreenIds(page);
    }
    qDebug() << __func__ << dataHolder.itemIdsInPageGrid;
    saveItemDetailsToDatabase(item->getId());
    updateItemIdsInHomeScreenPageToDatabase(page);
    Q_EMIT q->itemAdded(item->getId(), page);
}

void DesktopDataProviderPrivate::removeItemFromSet(int itemId, int setId, int pageIndex)
{
    removeAppIdFromSet(itemId, setId, pageIndex);
    qDebug() << __func__ << dataHolder.itemIdsInSet.value(setId);
    removeItem(itemId);
}

void DesktopDataProviderPrivate::updateAfterRemoveFromHomeScreenSet(int setId, int pageIndex)
{
    if (pageIndex >= 0 && dataHolder.itemIdsInSet.value(setId).at(pageIndex).isEmpty()) {
        deleteEmptyPageFromSet(pageIndex, setId);
    }
    qDebug() << __func__ << dataHolder.itemIdsInSet;
    qDebug() << __func__ << dataHolder.allItemIdsInHomeScreen;
    qDebug() << __func__ << dataHolder.itemIdsInPageGrid;
}

void DesktopDataProviderPrivate::deleteEmptyPageFromSet(int emptyPageIndex, int setId)
{
    int setLastPageIndex = dataHolder.itemIdsInSet.value(setId).count() - 1;
    for (int page = emptyPageIndex; page < setLastPageIndex; ++page) {
        dataHolder.itemIdsInSet[setId][page] = dataHolder.itemIdsInSet.value(setId).at(page + 1);
    }
    dataHolder.itemIdsInSet[setId].removeAt(setLastPageIndex);
    database->updateSetData(setId, dataHolder.itemIdsInSet.value(setId));
}

void DesktopDataProviderPrivate::removeItemFromHomeScreen(int itemId, int pageIndex)
{
    removeAppIdFromHomeScreen(itemId, pageIndex);
    removeItem(itemId);
}

void DesktopDataProviderPrivate::resetItemIdInPageGrid(int itemId)
{
    for (auto &itemIdsOnePage : dataHolder.itemIdsInPageGrid) {
        for (auto &ids : itemIdsOnePage) {
            std::replace_if(ids.begin(), ids.end(), [itemId](int id) {
                return id == itemId;
            }, -1);
        }
    }
}

void DesktopDataProviderPrivate::replaceIdInPageGrid(int pageIndex, int idBeforeChange, const int idAfterChange)
{
    Q_ASSERT(dataHolder.itemIdsInPageGrid.contains(pageIndex));
    for (auto &ids : dataHolder.itemIdsInPageGrid[pageIndex]) {
        std::replace_if(ids.begin(), ids.end(), [idBeforeChange](int id) {
            return id == idBeforeChange;
        }, idAfterChange);
    }
}

void DesktopDataProviderPrivate::loadItemsFromDatabase()
{
    dataHolder.allItems = database->queryAllItemData();
    dataHolder.allItemIdsInHomeScreen = database->queryAllItemIdsInHomeScreen();
    dataHolder.itemIdsInSet = database->queryAllItemIdsInSet();

    auto it = dataHolder.allItems.constBegin();

    while (it != dataHolder.allItems.constEnd()) {
        if (it.value()->getType() == UKUI::Type::Widget) {
            dataHolder.appWidgetIds.append(it.key());
        }
        ++ it;
    }
}

void DesktopDataProviderPrivate::loadNewInstalledApp()
{
    auto localDesktopFiles = m_desktopFileManager.localDesktopFiles();
    for (auto &desktopFile : qAsConst(localDesktopFiles)) {
        int appId = DesktopItemHelper::appIdByDesktopFromAllItems(desktopFile, dataHolder.allItems);
        if (appId == -1) {
            onDesktopFileAdded(desktopFile);
        }
    }
}

void DesktopDataProviderPrivate::checkItems()
{
    auto allItemIds = dataHolder.allItems.keys();
    for (auto &id : qAsConst(allItemIds)) {
        switch (dataHolder.allItems.value(id)->getType()) {
        case UKUI::Type::Group:
            checkGroupItem(id);
            break;
        case UKUI::Type::Icon: {
            checkAppIconItem(id);
            break;
        }
        default: break;
        }
    }
}

void DesktopDataProviderPrivate::updateTaskbarItems()
{
    if (!dataHolder.itemIdsInSet.contains(kTaskbarGroupId)) {
        return;
    }
    auto taskbarItemIds = dataHolder.itemIdsInSet.value(kTaskbarGroupId).constFirst();
    QStringList taskBarAppDesktopFiles;
    for (auto &itemId : qAsConst(taskbarItemIds)) {
        auto *appItem = static_cast<IconItem *>(dataHolder.allItems.value(itemId));
        if (appItem) {
            taskBarAppDesktopFiles.append(appItem->getDesktopName());
        }
    }
    m_taskbarAppManager.checkAppDesktopfiles(taskBarAppDesktopFiles);
}

void DesktopDataProviderPrivate::checkAppIconItem(int itemId)
{
    UKUI::IconItem *iconItem = static_cast<UKUI::IconItem*>(dataHolder.allItems.value(itemId));
    if (!iconItem) {
        return;
    }
    QString desktopFile = iconItem->getDesktopName();
    if (!m_desktopFileManager.localDesktopFiles().contains(desktopFile)) {
        removeItemByDesktopFile(desktopFile);
        return;
    }
    KDesktopFile kDesktopFile(desktopFile);
    QString appName = kDesktopFile.readName();
    if (iconItem->getName() != appName) {
        iconItem->setName(appName);
    }
}

void DesktopDataProviderPrivate::checkGroupItem(int groupId)
{
    if (!dataHolder.itemIdsInSet.contains(groupId)) {
        return;
    }
    auto ids = dataHolder.itemIdsInSet.value(groupId);
    for (auto &idsInPage : qAsConst(ids)) {
        for (auto &id : qAsConst(idsInPage)) {
            checkAppIconItem(id);
        }
    }
}

void DesktopDataProviderPrivate::placeItemsFromDatabase()
{
    using ItemType = UKUI::Type;
    for (int page=0;
         page < dataHolder.allItemIdsInHomeScreen.count();
         ++page) {
        adjustDesktopAppIdOrderAfterUpdateHomeScreenIds(page);

        for (int itemIndex=0;
             itemIndex < dataHolder.allItemIdsInHomeScreen.value(page).count();
             ++ itemIndex) {

            auto itemId = dataHolder.allItemIdsInHomeScreen.value(page).at(itemIndex);
            if (!dataHolder.allItems.contains(itemId)) {
                qWarning() << QString("Place item error, the item %1 exists in page table, "
                              "but dose not exist in itemlist table.").arg(itemId);
                continue;
            }
            auto *item = dataHolder.allItems.value(itemId);
            auto placeholder = item->getPlaceHolder();
            placeItem(itemId, page, placeholder.constFirst(), placeholder.constLast());
        }
    }
}

void DesktopDataProviderPrivate::placeItem(int itemId, int page, int rowSpan, int columnSpan)
{
    if (!dataHolder.itemIdsInPageGrid.contains(page)) {
        QList<QList<int> > itemIdsGrid;
        for (int row=0; row < desktopRow; ++row) {
            QList<int> columnItemIds;
            for (int col=0; col < desktopColumn; ++col) {
                columnItemIds.append(-1);
            }
            itemIdsGrid.append(columnItemIds);
        }
        dataHolder.itemIdsInPageGrid.insert(page, itemIdsGrid);
    }
    auto &itemIds = dataHolder.itemIdsInPageGrid[page];
    for (int row=0; row < desktopRow; ++row) {
        for (int col=0; col < desktopColumn; ++col) {
            if (itemIds.at(row).at(col) != -1) {
                continue;
            }
            if (row + rowSpan > desktopRow ||
                    col + columnSpan > desktopColumn) {
                continue;
            }
            for (int placeRow=row; placeRow < row+rowSpan; ++placeRow) {
                for (int placeCol=col; placeCol < col+columnSpan; ++placeCol) {
                    itemIds[placeRow][placeCol] = itemId;
                }
            }
            return;
        }
    }
}

void DesktopDataProviderPrivate::saveItemsToDatabase()
{
    auto allItemIds = dataHolder.allItems.keys();
    for (auto &itemId : qAsConst(allItemIds)) {
        saveItemDetailsToDatabase(itemId);
    }

    auto it = dataHolder.itemIdsInPageGrid.constBegin();
    while (it != dataHolder.itemIdsInPageGrid.constEnd()) {
        auto itemIdsInPageGrid = it.value();
        QList<int> idsToPage;
        for (auto &ids : itemIdsInPageGrid) {
            idsToPage += ids;
        }
        idsToPage = filterId(idsToPage);
        saveItemIdsInHomeScreenPageToDatabase(it.key(), idsToPage);
        dataHolder.allItemIdsInHomeScreen[it.key()] = idsToPage;
        ++ it;
    }
}

void DesktopDataProviderPrivate::saveItemDetailsToDatabase(int id)
{
    using IconItem = UKUI::IconItem;
    using GroupItem = UKUI::GroupItem;
    using WidgetItem = UKUI::Widget;
    using ContainerItem = UKUI::ContainerItem;
    UKUI::BaseItem *item = dataHolder.allItems.value(id);
    switch (item->getType()) {
    case UKUI::Type::Icon: {
        IconItem *iconItem = static_cast<IconItem *>(item);
        database->addAppItem(*iconItem);
        break;
    }
    case UKUI::Type::Group: {
        GroupItem *groupItem = static_cast<GroupItem *>(item);
        database->addGroupItem(*groupItem, dataHolder.itemIdsInSet.value(item->getId()));
        break;
    }
    case UKUI::Type::Widget: {
        WidgetItem *widgetItem = static_cast<WidgetItem *>(item);
        database->addWidgetItem(*widgetItem);
        break;
    }
    case UKUI::Type::Container: {
        ContainerItem *containerItem = static_cast<ContainerItem *>(item);
        database->addContainerItem(*containerItem, dataHolder.itemIdsInSet.value(item->getId()));
    }
    default:
        break;
    }
}

void DesktopDataProviderPrivate::saveItemIdsInHomeScreenPageToDatabase(int page, const QList<int> &itemIds)
{
    QList<int> ids;
    for (auto &id : itemIds) {
        ids.append(id);
    }
    database->addHomeScreenPage(page, ids);
}

void DesktopDataProviderPrivate::updateItemIdsInHomeScreenPageToDatabase(int page)
{
    if (!dataHolder.allItemIdsInHomeScreen.contains(page)) {
        qWarning() << QString("Update home screen page ids failed."
                              "The page %1 dose not exist.").arg(page);
        return;
    }
    database->updateHomeScreenPage(page, dataHolder.allItemIdsInHomeScreen.value(page));
}

QList<int> DesktopDataProviderPrivate::filterId(const QList<int> &ids)
{
    QList<int> retIds;
    for (auto &id : qAsConst(ids)) {
        if (retIds.contains(id) || id < 0) {
            continue;
        }
        retIds.push_back(id);
    }
    qDebug() << __func__ << retIds;
    return retIds;
}

void DesktopDataProviderPrivate::updateItemPositionAfterCreateGroup(int underItemId, int aboveItemId, int groupId)
{
    qDebug() << __func__ << "创建分组前" << dataHolder.itemIdsInPageGrid << dataHolder.allItemIdsInHomeScreen;
    int groupPage = itemPageIndex(underItemId);
    int aboveItemPage = itemPageIndex(aboveItemId);
    if (groupPage == -1 || aboveItemPage == -1) {
        return;
    }
    auto &itemIdsInPage = dataHolder.allItemIdsInHomeScreen[groupPage];
    for (int i=0; i<itemIdsInPage.count(); ++i) {
        if (itemIdsInPage.at(i) == underItemId) {
            itemIdsInPage[i] = groupId;
            break;
        }
    }
    dataHolder.allItemIdsInHomeScreen[aboveItemPage].removeOne(aboveItemId);
    database->updateHomeScreenPage(groupPage, dataHolder.allItemIdsInHomeScreen.value(groupPage));
    if (groupPage != aboveItemPage) {
        if (dataHolder.allItemIdsInHomeScreen.value(aboveItemPage).isEmpty()) {
            deleteEmptyPageFromHomeScreen(aboveItemPage);
        } else {
            database->updateHomeScreenPage(aboveItemPage, dataHolder.allItemIdsInHomeScreen.value(aboveItemPage));
        }
    }
    updateItemIdsInPageGridAfterCreateGroup(underItemId, aboveItemId, groupId);
    qDebug() << __func__ << "创建分组后" << dataHolder.itemIdsInPageGrid << dataHolder.allItemIdsInHomeScreen;
}

void DesktopDataProviderPrivate::updateItemIdsInPageGridAfterCreateGroup(int underItemId, int aboveItemId, int groupId)
{
    auto it = dataHolder.itemIdsInPageGrid.begin();
    while (it != dataHolder.itemIdsInPageGrid.end()) {
        auto &itemIdsInPage = *it;
        for (int row=0; row<desktopRow; ++row) {
            std::replace_if(itemIdsInPage[row].begin(), itemIdsInPage[row].end(),
                            [=](int id) {
                if (id == underItemId) {
                    return true;
                }
                if (id == aboveItemId) {
                    updateItemIdsInPageGridFromHomeScreenIds(it.key());
                }
                return false;
            }, groupId);
        }
        ++ it;
    }
}

void DesktopDataProviderPrivate::updateItemIdsInPageGridFromHomeScreenIds(int page)
{
    Q_ASSERT(dataHolder.allItemIdsInHomeScreen.contains(page) &&
             dataHolder.itemIdsInPageGrid.contains(page));

    auto &pageIds = dataHolder.itemIdsInPageGrid[page];
    for (auto &rowIds : pageIds) {
        for (auto &id : rowIds) {
            id = -1;
        }
    }
    //移动插件，松手后更新桌面数据，前台传过来的数据有问题
    //因为插件每次拖动只与一个应用进行交换
    //75为插件,占2列
    //拖动前数据：75,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22
    //插件拖动后：1,2,3,4,5,75,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22
    //正常顺序是：1,2,3,4,5,6,75,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22
    adjustDesktopAppIdOrderAfterUpdateHomeScreenIds(page);

    auto ids = dataHolder.allItemIdsInHomeScreen.value(page);
    for (auto &id : qAsConst(ids)) {
        if (!dataHolder.allItems.contains(id)) {
            dataHolder.allItemIdsInHomeScreen[page].removeOne(id);
            qWarning() << __func__ << "id not exist !!!";
            continue;
        }
        auto placeholder = dataHolder.allItems.value(id)->getPlaceHolder();
        int rowSpan = placeholder.constFirst();
        int colSpan = placeholder.constLast();
        placeItem(id, page, rowSpan, colSpan);
    }
}

void DesktopDataProviderPrivate::adjustDesktopAppIdOrderAfterUpdateHomeScreenIds(int page)
{
    auto ids = dataHolder.allItemIdsInHomeScreen.value(page);
    int appColumnCountInOneRow = 0;
    int appIndex = 0;
    for (int i = 0; i < ids.count(); ++i) {
        if (!dataHolder.allItems.value(ids.at(i))) {
            qWarning() << __func__ << "item is nullptr !!!";
            continue;
        }
        auto placeholder = dataHolder.allItems.value(ids.at(i))->getPlaceHolder();
        int colSpan = placeholder.constLast();
        if (colSpan > 1) {
            appIndex = i;
        }
        appColumnCountInOneRow += colSpan;
        if (appColumnCountInOneRow == desktopColumn) {
            appColumnCountInOneRow = 0;
        }
        if (appColumnCountInOneRow > desktopColumn) {
            appColumnCountInOneRow = 0;
            for (int j = appIndex; j < ids.count(); ++j) {
                if (!dataHolder.allItems.contains(ids.at(j))) {
                    dataHolder.allItemIdsInHomeScreen[page].removeOne(ids.at(j));
                    qWarning() << __func__ << "id not exist !!!";
                    continue;
                }
                auto itemPlaceholder = dataHolder.allItems.value(ids.at(j))->getPlaceHolder();
                if (itemPlaceholder.constLast() == 1) {
                    ids.swap(appIndex, j);
                    break;
                }
            }
        }
    }
    dataHolder.allItemIdsInHomeScreen[page] = ids;
}

std::tuple<bool, int, int> DesktopDataProviderPrivate::itemPositionInHomeScreen(int itemId) const
{
    auto it = dataHolder.allItemIdsInHomeScreen.constBegin();
    while (it != dataHolder.allItemIdsInHomeScreen.constEnd()) {
        auto itemIdsInPage = it.value();
        for (int index=0; index<itemIdsInPage.count(); ++index) {
            if (itemIdsInPage.at(index) == itemId) {
                return { true, index, it.key() };
            }
        }
        ++it;
    }
    return { false, -1, -1 };
}

int DesktopDataProviderPrivate::itemPageIndex(int itemId)
{
    bool exists;
    int itemIndex, pageIndex;
    std::tie(exists, itemIndex, pageIndex) = itemPositionInHomeScreen(itemId);
    if (exists) {
        return pageIndex;
    }

    return -1;
}

int DesktopDataProviderPrivate::appIdTaskbarChange(const QList<int> beforeChange, const QList<int> afterChange,
                                                   TaskbarAppManager::TaskbarAction action)
{
    int appCount = beforeChange.count() < afterChange.count() ? beforeChange.count() : afterChange.count();
    for (int i = 0; i < appCount; ++i) {
        if (beforeChange.at(i) != afterChange.at(i)) {
            if (action == TaskbarAppManager::Add) {
                return afterChange.at(i);
            }
            if (action == TaskbarAppManager::Delete) {
                return beforeChange.at(i);
            }
        }
    }
    if (action == TaskbarAppManager::Add) {
        return afterChange.last();
    }
    if (action == TaskbarAppManager::Delete) {
        return beforeChange.last();
    }
    return -1;
}

void DesktopDataProviderPrivate::moveAppIdFromSetToTaskbar(const int itemId, const QList<int> &taskbarAppIdsAfterChange)
{
    takeAppIdFromSet(itemId);
    moveAppIdToTaskbar(itemId, taskbarAppIdsAfterChange);
}

void DesktopDataProviderPrivate::moveAppIdFromHomeScreenToTaskbar(const int itemId, const QList<int> &taskbarAppIdsAfterChange)
{
    takeAppIdFromHomeScreen(itemId);
    moveAppIdToTaskbar(itemId, taskbarAppIdsAfterChange);
}

void DesktopDataProviderPrivate::moveAppIdFromTaskbarToHomeScreen(const int itemId)
{
    takeAppIdFromTaskbar(itemId);
    moveAppIdToHomeScreen(itemId);
}
void DesktopDataProviderPrivate::removeAppIdFromSet(int itemId, int setId, int pageIndex)
{
    if (!dataHolder.itemIdsInSet.contains(setId) ||
            dataHolder.itemIdsInSet.count() <= pageIndex ||
            pageIndex < 0) {
        qWarning() << __func__ << "setId or pageIndex not exists !!!";
        return;
    }
    dataHolder.itemIdsInSet[setId][pageIndex].removeOne(itemId);
    qDebug() << __func__ << dataHolder.itemIdsInSet;
    if (setId != kTaskbarGroupId) {
        updateAfterRemoveFromHomeScreenSet(setId, pageIndex);
    }
    database->updateSetData(setId, dataHolder.itemIdsInSet.value(setId));
}

void DesktopDataProviderPrivate::removeAppIdFromHomeScreen(int itemId, int pageIndex)
{
    if (!dataHolder.allItems.contains(itemId) ||
            dataHolder.itemIdsInPageGrid.count() <= pageIndex ||
            pageIndex < 0) {
        qWarning() << __func__ << "pageIndex is invalid ! ! !";
        return;
    }
    dataHolder.allItemIdsInHomeScreen[pageIndex].removeOne(itemId);
    if (dataHolder.allItemIdsInHomeScreen.value(pageIndex).isEmpty()) {
        deleteEmptyPageFromHomeScreen(pageIndex);
    } else {
        resetItemIdInPageGrid(itemId);
    }
    updateItemIdsInHomeScreenPageToDatabase(pageIndex);
    qDebug() << __func__ << "backend" << dataHolder.allItemIdsInHomeScreen;
    qDebug() << __func__ <<"backend" << dataHolder.itemIdsInPageGrid;
}

void DesktopDataProviderPrivate::deleteEmptyPageFromHomeScreen(int emptyPageIndex)
{
    int homePageLastPageIndex = dataHolder.allItemIdsInHomeScreen.count() - 1;
    for (int page = emptyPageIndex; page < homePageLastPageIndex; ++page) {
        dataHolder.allItemIdsInHomeScreen[page] = dataHolder.allItemIdsInHomeScreen.value(page + 1);
        updateItemIdsInPageGridFromHomeScreenIds(page);
    }
    dataHolder.allItemIdsInHomeScreen.remove(homePageLastPageIndex);
    dataHolder.itemIdsInPageGrid.remove(homePageLastPageIndex);
    database->deletePageData(homePageLastPageIndex);
}

void DesktopDataProviderPrivate::takeAppIdFromSet(int itemId)
{
    Q_Q(DesktopDataProvider);
    bool existed;
    int setId, pageIndex;
    std::tie(existed, setId, pageIndex) =
            DesktopItemHelper::itemPositionInSet(itemId, dataHolder.itemIdsInSet);
            qDebug() << __func__ << itemId << setId << pageIndex;
    removeAppIdFromSet(itemId, setId, pageIndex);
    Q_EMIT q->appIdTakedFromSet(itemId, setId);
}

void DesktopDataProviderPrivate::takeAppIdFromHomeScreen(int itemId)
{
    Q_Q(DesktopDataProvider);
    int appIdPageIndex = itemPageIndex(itemId);
    qDebug() << __func__ << itemId;
    removeAppIdFromHomeScreen(itemId, appIdPageIndex);
    Q_EMIT q->appIdTakedFromHomeScreen(itemId, appIdPageIndex);
}

void DesktopDataProviderPrivate::takeAppIdFromTaskbar(int itemId)
{
    Q_Q(DesktopDataProvider);
    dataHolder.itemIdsInSet[kTaskbarGroupId][0].removeOne(itemId);
    database->updateSetData(kTaskbarGroupId, dataHolder.itemIdsInSet.value(kTaskbarGroupId));
    qDebug() << __func__ << dataHolder.itemIdsInSet.value(kTaskbarGroupId);
    Q_EMIT q->appIdTakedFromTaskbar(itemId);
}

void DesktopDataProviderPrivate::moveAppIdToHomeScreen(int itemId)
{
    Q_Q(DesktopDataProvider);
    AppItemPositionCalculator postionCalculator;
    int page, row, col;
    std::tie(page, row, col) =
            postionCalculator.calculationPosition(desktopRow, desktopColumn,
                                          dataHolder.itemIdsInPageGrid, 1, 1);
    placeItem(itemId, page, 1, 1);

    dataHolder.allItemIdsInHomeScreen[page].push_back(itemId);
    updateItemIdsInPageGridFromHomeScreenIds(page);
    updateItemIdsInHomeScreenPageToDatabase(page);
    qDebug() << __func__ << "backend" << dataHolder.allItemIdsInHomeScreen.value(page);
    qDebug() << __func__ << "backend" << dataHolder.itemIdsInPageGrid.value(page);
    Q_EMIT q->itemAdded(itemId, page);
}

void DesktopDataProviderPrivate::moveAppIdToTaskbar(const int itemId, const QList<int> &pageListAfterChange)
{
    Q_Q(DesktopDataProvider);
    int insertPosIndex = -1;
    QList<int> pageListBeforeChange = dataHolder.itemIdsInSet.value(kTaskbarGroupId).at(0);
    for (int i = 0; i < pageListBeforeChange.count(); ++i) {
        if (pageListBeforeChange.at(i) != pageListAfterChange.at(i)) {
            insertPosIndex = i;
            break;
        }
    }
    if (insertPosIndex < 0) {
        insertPosIndex = pageListBeforeChange.count();
    }
    dataHolder.itemIdsInSet[kTaskbarGroupId][0] = pageListAfterChange;
    qDebug() << "backend" << __func__ << dataHolder.itemIdsInSet.value(kTaskbarGroupId).at(0);
    database->updateSetData(kTaskbarGroupId, dataHolder.itemIdsInSet[kTaskbarGroupId]);
    Q_EMIT q->taskbarAppIdAdded(itemId, insertPosIndex);
}

void DesktopDataProviderPrivate::taskbarAppAdded(const QList<ItemId> &newIds)
{
    int itemId = appIdTaskbarChange(dataHolder.itemIdsInSet.value(kTaskbarGroupId).at(0),
                                    newIds, TaskbarAppManager::Add);
    qDebug() << itemId;
    if (DesktopItemHelper::itemExistsInSet(itemId, dataHolder.itemIdsInSet)) {
        moveAppIdFromSetToTaskbar(itemId, newIds);
    } else {
        moveAppIdFromHomeScreenToTaskbar(itemId, newIds);
    }
}

void DesktopDataProviderPrivate::taskbarAppDeleted(const QList<ItemId> &newIds)
{
    int itemId = appIdTaskbarChange(dataHolder.itemIdsInSet.value(kTaskbarGroupId).at(0),
                                    newIds, TaskbarAppManager::Delete);
    moveAppIdFromTaskbarToHomeScreen(itemId);
}

void DesktopDataProviderPrivate::taskbarAppMoved(const QList<ItemId> &newIds)
{
    Q_Q(DesktopDataProvider);
    dataHolder.itemIdsInSet[kTaskbarGroupId][0] = newIds;
    database->updateSetData(kTaskbarGroupId, dataHolder.itemIdsInSet.value(kTaskbarGroupId));
    qDebug() << __func__ << "backend" << dataHolder.itemIdsInSet.value(kTaskbarGroupId);
    Q_EMIT q->taskbarAppIdPosExchanged(dataHolder.itemIdsInSet.value(kTaskbarGroupId).at(0));

}

void DesktopDataProviderPrivate::saveNewGroupData(int groupId, const QString &groupName, const QList<int> &itemIds)
{
    int underItemId = itemIds.constFirst();
    int aboveItemId = itemIds.constLast();
    QString sourceGroupName, translatedGroupName;
    std::tie(sourceGroupName, translatedGroupName) = translateGroupName(groupName, underItemId, aboveItemId);
    QList<QList<int>> itemIdlistInGroup;
    itemIdlistInGroup.append(itemIds);
    dataHolder.allItems[groupId] = new UKUI::GroupItem(translatedGroupName, groupId);
    dataHolder.itemIdsInSet[groupId] = itemIdlistInGroup;
    database->addGroupItem(UKUI::GroupItem(DEFAULT_GROUP_NAME_PREFIX + sourceGroupName, groupId), itemIdlistInGroup);
}

std::tuple<QString, QString> DesktopDataProviderPrivate::translateGroupName(const QString &groupName, int underItemId, int aboveItemId)
{
    QString sourceGroupName, translatedGroupName;
    if (groupName.isEmpty()) {
        QList<int> categoryList;
        if (dataHolder.allItems.contains(underItemId)) {
            categoryList = dataHolder.allItems.value(aboveItemId)->getCategories();
            sourceGroupName = AppCategoryManager::categoryToString(categoryList.constFirst());
            translatedGroupName = translatedName(sourceGroupName);
        } else {
            sourceGroupName = AppCategoryManager::categoryToString(BaseItem::Categories::System);
            translatedGroupName = translatedName(sourceGroupName);
        }
    } else {
        sourceGroupName = groupName;
        translatedGroupName = translatedName(groupName);
    }

    return std::make_tuple(sourceGroupName, translatedGroupName);
}

void DesktopDataProviderPrivate::writeTaskbarDataToTaskbarPanelConf(const QList<int> &taskbarIds)
{
    QList<QString> taskbarDesktopNameList;
    for (int i = 0; i < taskbarIds.count(); ++i) {
        int itemId = taskbarIds.at(i);
        if (!dataHolder.allItems.contains(itemId)) {
            continue;
        }
        QString desktopName = static_cast<IconItem *>(dataHolder.allItems.value(itemId))->getDesktopName();
        taskbarDesktopNameList.push_back(desktopName);
    }
    m_taskbarAppManager.updatePanelConf(taskbarDesktopNameList);
    qDebug() << __func__ << taskbarDesktopNameList;
}

QString DesktopDataProviderPrivate::getDatabasePath(const QString &datebaseDir)
{
    auto startardPaths = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation);
    if (startardPaths.count() <= 0) {
        qWarning() << "The directory containing the configuration file was not found ! ! !";
        return QString();
    }
    QString dbPath = startardPaths.constFirst() + datebaseDir;
    QDir dir(dbPath);
    if (dir.exists()) {
        return dbPath + "/" + DATABASE_NAME;
    }
    bool createResult = dir.mkpath(dbPath);
    if (createResult) {
        return dbPath + "/" + DATABASE_NAME;
    }
    return QString();
}

}
